package com.xiongkw.excel;

import com.google.common.collect.Maps;
import com.xiongkw.excel.ExcelConstant;
import com.xiongkw.excel.ExportDesc;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFCellStyle;
import org.apache.poi.hssf.util.HSSFColor;
import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.xssf.usermodel.*;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;

/**
 * @Description:
 * @Author: xiongkw
 * @Date: 2019/3/28
 */
@Slf4j
public class ExcelWorkBookUtil {

    /**
     * description: 支持List中的对象嵌套List结构，并横向扩展
     *
     * @author: xiongkw
     * @date: 2019/3/28
     */
    public static XSSFWorkbook genWidthwiseExcel(List<?> list, Map<String, String> varibleTitleMap, Class classType) {
        try {
            XSSFWorkbook xssfWorkbook = new XSSFWorkbook();
            XSSFSheet sheet = xssfWorkbook.
                    createSheet("导出数据");

            Map<String, Integer> fieldMaxSizeMap = Maps.newHashMap();
            for (Object o : list) {
                getWidthwiseFieldsMaxSize(o, fieldMaxSizeMap);
            }

            List<String> fieldNames = new ArrayList<>();

            getWidthwiseExcelTitles(classType.newInstance(), fieldNames, null, fieldMaxSizeMap, varibleTitleMap);

            List<List<String>> rows = new ArrayList<>();
            list.forEach(a -> {
                List<String> subRows = new ArrayList<>();
                genWidthwiseExcelCells(a, subRows, fieldMaxSizeMap);
                rows.add(subRows);
            });


            rows.add(0, fieldNames);
            XSSFCellStyle bodyStyle = genBodyStyle(xssfWorkbook);
            XSSFCellStyle headStyle = genHeadStyle(xssfWorkbook);
            for (int i = 0; i < rows.size(); i++) {
                addRow(sheet, i, rows.get(i), headStyle, bodyStyle);
            }
            return xssfWorkbook;
        } catch (Exception e) {
            log.error("生成横向Excel异常：", e);
            throw new RuntimeException("生成横向Excel异常", e);
        }
    }

    /**
     * description: 支持List中的对象嵌套List结构，并纵向扩展
     *
     * @author: xiongkw
     * @date: 2019/3/28
     */
    public static XSSFWorkbook genLongitudinalExcel(List<?> list, Class classType, Class specificClassType) {
        try {
            XSSFWorkbook xssfWorkbook = new XSSFWorkbook();
            XSSFSheet sheet = xssfWorkbook.createSheet("导出数据");
            List<String> fieldNames = new ArrayList<>();
            getLongitudinalTitles(classType.newInstance(), specificClassType == null ? null : specificClassType.newInstance(), fieldNames);
            List<List<String>> rows = new ArrayList<>();
            list.forEach(a -> {
                List<List<String>> subRows = new ArrayList<>();
                genLongitudinalCells(a, subRows);
                rows.addAll(subRows);
            });
            rows.add(0, fieldNames);
            XSSFCellStyle bodyStyle = genBodyStyle(xssfWorkbook);
            XSSFCellStyle headStyle = genHeadStyle(xssfWorkbook);
            for (int i = 0; i < rows.size(); i++) {
                addRow(sheet, i, rows.get(i), headStyle, bodyStyle);
            }
            return xssfWorkbook;
        } catch (Exception e) {
            log.error("生成纵向Excel异常：", e);
            throw new RuntimeException("生成纵向Excel异常", e);
        }
    }

    /**
     * 根据List 填充sheet
     *
     * @param list
     * @param sheet
     * @param xssfWorkbook
     * @param classType
     */
    public static void exportDealWithSheet(List<?> list, XSSFSheet sheet, XSSFWorkbook xssfWorkbook, Class classType) {
        try {
            List<String> fieldNames = new ArrayList<>();
            getLongitudinalTitles(classType.newInstance(), null, fieldNames);
            List<List<String>> rows = new ArrayList<>();
            list.forEach(a -> {
                List<List<String>> subRows = new ArrayList<>();
                genLongitudinalCells(a, subRows);
                rows.addAll(subRows);
            });
            rows.add(0, fieldNames);
            XSSFCellStyle bodyStyle = genBodyStyle(xssfWorkbook);
            XSSFCellStyle headStyle = genHeadStyle(xssfWorkbook);
            for (int i = 0; i < rows.size(); i++) {
                addRow(sheet, i, rows.get(i), headStyle, bodyStyle);
            }
        } catch (Exception e) {
            log.error("导出异常", e);
        }
    }

    private static void getWidthwiseFieldsMaxSize(Object object, Map<String, Integer> fieldMaxSizeMap) {
        try {
            if (object == null) {
                return;
            }
            Field[] fields1 = object.getClass().getSuperclass().getDeclaredFields();
            Field[] fields2 = object.getClass().getDeclaredFields();
            Field[] resultField = ArrayUtils.addAll(fields1, fields2);
            for (Field field : resultField) {
                ExportDesc exportDesc = field.getAnnotation(ExportDesc.class);
                if (exportDesc == null) {
                    continue;
                }
                field.setAccessible(true);
                if (exportDesc.type() == ExcelConstant.EXPORT_LIST_FIELD) {
                    String fieldName = exportDesc.fieldName();
                    if (StringUtils.isBlank(fieldName)) {
                        throw new RuntimeException("List标题名称fieldName不能为空");
                    }
                    List list = (List) field.get(object);
                    if (CollectionUtils.isNotEmpty(list)) {
                        if (fieldMaxSizeMap.containsKey(fieldName)) {
                            Integer count = fieldMaxSizeMap.get(fieldName);
                            if (list.size() > count) {
                                fieldMaxSizeMap.put(fieldName, list.size());
                            }
                        } else {
                            fieldMaxSizeMap.put(fieldName, list.size());
                        }
                    } else {
                        if (!fieldMaxSizeMap.containsKey(fieldName)) {
                            fieldMaxSizeMap.put(fieldName, 1);
                        }
                    }
                }
            }
        } catch (Exception e) {
            log.error("导出对象解析异常", e);
            throw new RuntimeException("导出对象解析异常", e);
        }
    }

    private static void getWidthwiseExcelTitles(Object object, List<String> fieldNames, Integer identify, Map<String, Integer> fieldMaxSizeMap, Map<String, String> varibleTitleMap) {
        try {
            Field[] fields1 = object.getClass().getSuperclass().getDeclaredFields();
            Field[] fields2 = object.getClass().getDeclaredFields();
            Field[] resultField = ArrayUtils.addAll(fields1, fields2);
            for (Field field : resultField) {
                ExportDesc exportDesc = field.getAnnotation(ExportDesc.class);
                if (exportDesc == null) {
                    continue;
                }
                field.setAccessible(true);
                if (exportDesc.type() == ExcelConstant.EXPORT_COMMON_FIELD) {
                    if (identify == null) {
                        fieldNames.add(String.format(exportDesc.fieldName(), ""));
                    } else {
                        fieldNames.add(String.format(exportDesc.fieldName(), identify));
                    }
                }
                if (exportDesc.type() == ExcelConstant.EXPORT_OBJECT_FIELD) {
                    getWidthwiseExcelTitles(field.getType().newInstance(), fieldNames, identify, fieldMaxSizeMap, varibleTitleMap);
                }
                if (exportDesc.type() == ExcelConstant.EXPORT_LIST_FIELD) {
                    Type genericType = field.getGenericType();
                    if (genericType == null) {
                        throw new RuntimeException("List集合未指定泛型类型");
                    }
                    // 如果是泛型参数的类型
                    if (genericType instanceof ParameterizedType) {
                        ParameterizedType pt = (ParameterizedType) genericType;
                        //得到泛型里的class类型对象
                        Class<?> genericClazz = (Class<?>) pt.getActualTypeArguments()[0];
                        Integer count = fieldMaxSizeMap.get(exportDesc.fieldName());
                        if (count == null) {
                            getWidthwiseExcelTitles(genericClazz.newInstance(), fieldNames, null, fieldMaxSizeMap, varibleTitleMap);
                        } else {
                            for (int i = 1; i <= count; i++) {
                                getWidthwiseExcelTitles(genericClazz.newInstance(), fieldNames, i, fieldMaxSizeMap, varibleTitleMap);
                            }
                        }
                    } else {
                        throw new RuntimeException("List集合未指定泛型类型");
                    }
                }
                if (exportDesc.type() == ExcelConstant.EXPORT_VARIBLE_TITLE_FIELD) {
                    String varibleTitelName = varibleTitleMap.get(exportDesc.fieldName());
                    if (StringUtils.isBlank(varibleTitelName)) {
                        log.error("可变标题变量未设置,varibleTitel:" + exportDesc.fieldName());
                        throw new RuntimeException("解析Excel title异常");
                    }
                    fieldNames.add(varibleTitelName);
                }
            }
        } catch (Exception e) {
            log.error("解析Excel title异常", e);
            throw new RuntimeException("解析Excel title异常", e);
        }
    }

    private static void genWidthwiseExcelCells(Object object, List<String> rows, Map<String, Integer> fieldMaxSizeMap) {
        try {
            if (rows == null) {
                rows = new ArrayList<>();
            }
            Field[] fields1 = object.getClass().getSuperclass().getDeclaredFields();
            Field[] fields2 = object.getClass().getDeclaredFields();
            Field[] resultField = ArrayUtils.addAll(fields1, fields2);
            for (Field field : resultField) {
                ExportDesc exportDesc = field.getAnnotation(ExportDesc.class);
                if (exportDesc == null) {
                    continue;
                }
                field.setAccessible(true);
                if (exportDesc.type() == ExcelConstant.EXPORT_COMMON_FIELD || exportDesc.type() == ExcelConstant.EXPORT_VARIBLE_TITLE_FIELD) {
                    rows.add(getSimpleColumn(field, object));
                }
                if (exportDesc.type() == ExcelConstant.EXPORT_OBJECT_FIELD || exportDesc.type() == ExcelConstant.EXPORT_FATHER_OBJECT_FIELD) {
                    Object comObj = field.get(object);
                    if (comObj != null) {
                        genWidthwiseExcelCells(comObj, rows, fieldMaxSizeMap);
                    } else {
                        genWidthwiseExcelCells(field.getType().newInstance(), rows, fieldMaxSizeMap);
                    }
                }
                if (exportDesc.type() == ExcelConstant.EXPORT_LIST_FIELD) {
                    List list = (List) field.get(object);
                    Integer count = fieldMaxSizeMap.get(exportDesc.fieldName());
                    Type genericType = field.getGenericType();
                    if (genericType == null) {
                        throw new RuntimeException("List集合未指定泛型类型");
                    }
                    List<String> cells = new ArrayList<>();
                    // 如果是泛型参数的类型
                    if (genericType instanceof ParameterizedType) {
                        ParameterizedType pt = (ParameterizedType) genericType;
                        //得到泛型里的class类型对象
                        Class<?> genericClazz = (Class<?>) pt.getActualTypeArguments()[0];
                        getSimpleObjectCells(genericClazz.newInstance(), cells);
                    } else {
                        throw new RuntimeException("List集合未指定泛型类型");
                    }
                    if (CollectionUtils.isEmpty(list)) {
                        for (int i = 0; i < count; i++) {
                            rows.addAll(cells);
                        }
                        continue;

                    }
                    for (Object o : list) {
                        genWidthwiseExcelCells(o, rows, fieldMaxSizeMap);
                    }
                    if (list.size() < count) {
                        for (int i = 0; i < (count - list.size()); i++) {
                            rows.addAll(cells);
                        }
                    }

                }
            }
        } catch (Exception e) {
            log.error("导出对象解析异常", e);
            throw new RuntimeException("导出对象解析异常", e);
        }
    }

    private static void getLongitudinalTitles(Object object, Object specificObject, List<String> fieldNames) {
        try {
            Field[] fields1 = object.getClass().getSuperclass().getDeclaredFields();
            Field[] fields2 = object.getClass().getDeclaredFields();
            Field[] resultField = ArrayUtils.addAll(fields1, fields2);
            for (Field field : resultField) {
                ExportDesc exportDesc = field.getAnnotation(ExportDesc.class);
                if (exportDesc == null) {
                    continue;
                }
                field.setAccessible(true);
                if (exportDesc.type() == ExcelConstant.EXPORT_COMMON_FIELD) {
                    fieldNames.add(exportDesc.fieldName());
                }
                if (exportDesc.type() == ExcelConstant.EXPORT_OBJECT_FIELD) {
                    getLongitudinalTitles(field.getType().newInstance(), specificObject, fieldNames);
                }
                if (exportDesc.type() == ExcelConstant.EXPORT_LIST_FIELD) {
                    Type genericType = field.getGenericType();
                    if (genericType == null) {
                        throw new RuntimeException("List集合未指定泛型类型");
                    }
                    // 如果是泛型参数的类型
                    if (genericType instanceof ParameterizedType) {
                        ParameterizedType pt = (ParameterizedType) genericType;
                        //得到泛型里的class类型对象
                        Class<?> genericClazz = (Class<?>) pt.getActualTypeArguments()[0];
                        getLongitudinalTitles(genericClazz.newInstance(), specificObject, fieldNames);
                    } else {
                        throw new RuntimeException("List集合未指定泛型类型");
                    }
                }
                if (exportDesc.type() == ExcelConstant.EXPORT_FATHER_OBJECT_FIELD) {
                    getLongitudinalTitles(specificObject, specificObject, fieldNames);
                }
            }
        } catch (Exception e) {
            log.error("解析Excel title异常", e);
            throw new RuntimeException("解析Excel title异常", e);
        }
    }

    private static void genLongitudinalCells(Object object, List<List<String>> rows) {
        try {
            List<String> values;
            if (rows.size() == 0) {
                values = new ArrayList<>();
                rows.add(values);
            } else {
                values = rows.get(0);
            }
            Field[] fields1 = object.getClass().getSuperclass().getDeclaredFields();
            Field[] fields2 = object.getClass().getDeclaredFields();
            Field[] resultField = ArrayUtils.addAll(fields1, fields2);
            for (Field field : resultField) {
                ExportDesc exportDesc = field.getAnnotation(ExportDesc.class);
                if (exportDesc == null) {
                    continue;
                }
                field.setAccessible(true);
                if (exportDesc.type() == ExcelConstant.EXPORT_COMMON_FIELD) {
                    values.add(getSimpleColumn(field, object));
                }
                if (exportDesc.type() == ExcelConstant.EXPORT_OBJECT_FIELD || exportDesc.type() == ExcelConstant.EXPORT_FATHER_OBJECT_FIELD) {
                    Object comObj = field.get(object);
                    if (comObj != null) {
                        genLongitudinalCells(comObj, rows);
                    } else {
                        genLongitudinalCells(field.getType().newInstance(), rows);
                    }
                }
                if (exportDesc.type() == ExcelConstant.EXPORT_LIST_FIELD) {
                    List list = (List) field.get(object);
                    if (CollectionUtils.isEmpty(list)) {
                        Type genericType = field.getGenericType();
                        if (genericType == null) {
                            throw new RuntimeException("List集合未指定泛型类型");
                        }
                        // 如果是泛型参数的类型
                        if (genericType instanceof ParameterizedType) {
                            ParameterizedType pt = (ParameterizedType) genericType;
                            //得到泛型里的class类型对象
                            Class<?> genericClazz = (Class<?>) pt.getActualTypeArguments()[0];
                            List<String> cells = new ArrayList<>();
                            getSimpleObjectCells(genericClazz.newInstance(), cells);
                            for (int i = 0; i < rows.size(); i++) {
                                rows.get(i).addAll(cells);
                            }
                            continue;
                        } else {
                            throw new RuntimeException("List集合未指定泛型类型");
                        }
                    }
                    int size = values.size();
                    int row = rows.size();
                    for (int index = 0; index < list.size(); index++) {
                        Object o = list.get(index);
                        List<String> cells = new ArrayList<>();
                        getSimpleObjectCells(o, cells);
                        if (CollectionUtils.isEmpty(cells)) {
                            continue;
                        }
                        if (index <= (row - 1)) {
                            rows.get(index).addAll(cells);
                        } else {
                            List l = new ArrayList();
                            //补位前面的空行
                            genEmptyList(l, size);
                            l.addAll(cells);
                            rows.add(l);
                        }
                    }
                    if (rows.size() > list.size()) {
                        int lastIndex = rows.get(0).size();
                        for (int i = list.size(); i < rows.size(); i++) {
                            List<String> currentRow = rows.get(i);
                            //如果前面有List集合比当前list集合size大，则补位后面的空行
                            genEmptyList(currentRow, lastIndex - currentRow.size());
                        }
                    }
                }
            }
        } catch (Exception e) {
            log.error("导出对象解析异常", e);
            throw new RuntimeException("导出对象解析异常", e);
        }
    }

    private static void getSimpleObjectCells(Object o, List<String> cells) throws IllegalAccessException, InstantiationException {
        Field[] fields = o.getClass().getDeclaredFields();
        for (Field field : fields) {
            ExportDesc exportDesc = field.getAnnotation(ExportDesc.class);
            if (exportDesc == null) {
                continue;
            }
            field.setAccessible(true);
            if (exportDesc.type() == ExcelConstant.EXPORT_COMMON_FIELD) {
                cells.add(getSimpleColumn(field, o));
            }
            if (exportDesc.type() == ExcelConstant.EXPORT_OBJECT_FIELD) {
                Object comObj = field.get(o);
                if (comObj != null) {
                    getSimpleObjectCells(comObj, cells);
                } else {
                    getSimpleObjectCells(field.getType().newInstance(), cells);
                }
            }
        }
    }

    private static String getSimpleColumn(Field field, Object object) throws IllegalAccessException {
        Object value = field.get(object);
        String cellValue;
        if (value != null) {
            if (field.getGenericType().toString().equals(
                    "class java.util.Date")) {
                Date val = (Date) value;
                cellValue = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(val);
            } else {
                cellValue = value.toString();
            }
        } else {
            cellValue = "";
        }
        return cellValue;
    }

    private static void genEmptyList(List<String> list, int size) {
        for (int i = 0; i < size; i++) {
            list.add("");
        }
    }

    private static void addRow(XSSFSheet sheet, int rowNum, List<String> list, XSSFCellStyle headStyle, XSSFCellStyle bodyStyle) {
        XSSFRow row = sheet.createRow(rowNum);
        if (rowNum == 0) {
            row.setHeight((short) (200 * 4));
        }
        for (int i = 0; i < list.size(); i++) {
            XSSFCell cell = row.createCell(i);
            if (rowNum == 0) {
                sheet.setColumnWidth(i, 256 * 25 + 184);
                cell.setCellStyle(headStyle);
            } else {
                cell.setCellStyle(bodyStyle);
            }
            cell.setCellValue(list.get(i));
        }
    }

    private static XSSFCellStyle genBodyStyle(XSSFWorkbook workbook) {
        XSSFCellStyle bodyStyle = workbook.createCellStyle();
        bodyStyle.setWrapText(true);
        bodyStyle.setAlignment(HSSFCellStyle.ALIGN_LEFT);
        bodyStyle.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);
        return bodyStyle;
    }

    public static XSSFCellStyle genHeadStyle(XSSFWorkbook workbook) {
        XSSFCellStyle headStyle = workbook.createCellStyle();
        headStyle.setWrapText(true);
        headStyle.setAlignment(HSSFCellStyle.ALIGN_LEFT);
        headStyle.setVerticalAlignment(HSSFCellStyle.VERTICAL_CENTER);
        headStyle.setFillPattern(HSSFCellStyle.FINE_DOTS);
        headStyle.setFillForegroundColor(new HSSFColor.PALE_BLUE().getIndex());
        headStyle.setBorderBottom(BorderStyle.THIN);// 底部边框线样式(细实线)
        headStyle.setBottomBorderColor(new HSSFColor.BLACK().getIndex());// 底部边框线颜色
        headStyle.setBorderLeft(BorderStyle.THIN);// 左边框线样式(细实线)
        headStyle.setLeftBorderColor(new HSSFColor.BLACK().getIndex());// 左边框线颜色
        headStyle.setBorderRight(BorderStyle.THIN);// 右边框线样式(细实线)
        headStyle.setRightBorderColor(new HSSFColor.BLACK().getIndex());// 右边框线颜色
        headStyle.setBorderTop(BorderStyle.THIN);// 顶部边框线样式(细实线)
        headStyle.setTopBorderColor(new HSSFColor.BLACK().getIndex());// 顶部边框线颜色
        return headStyle;
    }
}
